import {
  indexPaginateBuilder,
  showBuilderEntity,
} from '@libs/common/repository';
import { isPro } from '@libs/common/utils';
import { getRandomRange } from '@libs/common/utils/math';
import { forwardRef, Inject, Injectable } from '@nestjs/common';
import { Cron } from '@nestjs/schedule';
import { ArrayToObject } from '@shencom/utils-array';
import { FindOptionsWhere, Repository } from 'typeorm';

import { PointTypeEnum } from '@/constant.enum';
import { Member } from '@/dingding/member/entities/member.entity';
import { MemberService } from '@/dingding/member/member.service';

import { PointsHistoryService } from '../points-history/points-history.service';
import {
  CountPointsDto,
  CreatePointsDto,
  FindPointsDto,
  IndexPointsDto,
  ShowPointsDto,
} from './dto/points.dto';
import { Points } from './entities/points.entity';
import { ProviderKey } from './points.providers';
import { countPoints } from './utils';

// TODO: 通过一个数据表进行管理
const deptIds = {
  '5503689': 0.8,
  '5503860': 1,
  '28690221': 1,
  '71469227': 1,
  '96543002': 1,
  '96596002': 1,
  '153323691': 0.6,
  '298440790': 0.8,
  '306944007': 0.6,
  '423060007': 0.5,
  '431176084': 0.5,
  '509401380': 1,
  '570922195': 0.8,
  '600605709': 1,
  '607115055': 0.5,
  '710763439': 0.5,
  '837153409': 0.4,
  '842983797': 0.5,
  '843148560': 0.4,
  '895006157': 0.5,
  '895107146': 0.6,
  '912647050': 0.8,
};

@Injectable()
export class PointsService {
  @Inject(ProviderKey)
  private repository: Repository<Points>;

  // 解决相互引用问题
  @Inject(forwardRef(() => PointsHistoryService))
  private pointsHistoryService: PointsHistoryService;

  @Inject(MemberService)
  private memberService: MemberService;

  async index(dto: IndexPointsDto) {
    const res = await indexPaginateBuilder({
      dto,
      repository: this.repository,
    });
    return res;
  }

  show<D extends ShowPointsDto = ShowPointsDto>(dto: D) {
    return showBuilderEntity(this.repository, dto);
  }

  async count(dto: CountPointsDto) {
    if (!dto.userId) {
      throw new Error('用户ID不能为空');
    }

    let userName = dto.userName;

    if (!userName) {
      const user = await this.memberService.findOne({ userId: dto.userId });
      userName = user?.name;
    }

    if (!userName) {
      throw new Error('用户不存在');
    }

    const res = await this.pointsHistoryService.find({ userId: dto.userId });

    const points = await this.findOne({ userId: dto.userId });

    const entities = countPoints(res);
    if (points?.id) {
      entities.id = points.id;
    }
    entities.userId = dto.userId;
    entities.userName = userName;

    return this.repository.save(entities);
  }

  async findUserPoints(userId: string) {
    if (!userId) {
      throw new Error('用户ID不能为空');
    }
    await this.count({ userId: userId });
    return this.repository.findOne({
      where: { userId },
      select: ['id', 'historyPoints', 'usedPoints', 'points'],
    });
  }

  async createBase(dto: {
    points: CreatePointsDto['points'];
    desc: CreatePointsDto['desc'];
    userId: string;
    userName: string;
    operatorUserId: string;
    operatorUserName: string;
    type: 'system' | 'manual';
  }) {
    const res = await this.pointsHistoryService.create({
      points: dto.points,
      desc: dto.desc,
      type: dto.type,
      userId: dto.userId,
      userName: dto.userName,
      operatorId: dto.operatorUserId,
      operatorName: dto.operatorUserName,
    });

    return res.id;
  }

  async create(dto: CreatePointsDto, operator: SC.User.SysInfo) {
    let operatorUser: null | Member = null;
    if (operator.id) {
      // 根据 operator.id 获取用户信息
      operatorUser = await this.memberService.findOne({ userId: operator.id });
    }

    if (!operatorUser) {
      throw new Error('操作者不存在');
    }

    if (
      !['1000889434306048000', '1458182516067864576'].includes(operator.id) &&
      isPro
    ) {
      throw new Error('非管理员禁止操作');
    }

    const user = await this.memberService.findOne({ userId: dto.userId });

    if (!user) {
      throw new Error('用户不存在');
    }

    return this.createBase({
      points: dto.points,
      desc: dto.desc,
      type: PointTypeEnum['手动'],
      userId: user.userId,
      userName: user.name,
      operatorUserId: operatorUser.userId,
      operatorUserName: operatorUser.name,
    });
  }

  async findOne(dto: FindPointsDto) {
    const where: FindOptionsWhere<Points> = {};
    if (dto.userId) {
      where.userId = dto.userId;
    } else {
      return null;
    }

    const res = await this.repository.findOne({
      where: where,
    });

    return res;
  }

  /**
   * 定时获取钉钉前一天的考勤数据
   * 定时在每天凌晨 2 点执行前一天的考勤数据
   * ```
   * 对于表达式 0 0 02 * * *，各部分的含义如下：
   * 秒：0 - 在每分钟的第 0 秒触发。
   * 分：0 - 在每小时的第 0 分触发。
   * 时：02 - 在每天的凌晨 2 点触发。
   * 日：* - 每天。
   * 月：* - 每个月。
   * 星期几：* - 每天。
   * ```
   */
  @Cron('0 0 02 * * *')
  async init() {
    // 获取成员信息
    const memberList = await this.memberService.find({ status: 1 });

    const pointsUserList = ArrayToObject(
      await this.repository.find(),
      'userId',
    );

    let addPointsUserList: (CreatePointsDto & { name: string })[] = [];

    for (const key of memberList) {
      const randomInt = getRandomRange(10, 20);

      // 获取用户的第一个部门
      const deptId = key.deptIdList
        .split(',')
        .filter(Boolean)
        .at(0) as keyof typeof deptIds;

      if (!pointsUserList[key.userId]) {
        const percent = deptIds[deptId] || 0.5;
        const points = Math.floor(randomInt * percent * 100);

        addPointsUserList.push({
          points,
          userId: key.userId,
          name: key.name,
          desc: '新用户注册随机赠送积分',
        });
      }
    }

    const pointsHistory = await this.pointsHistoryService.findUserIds({
      userId: memberList.map((item) => item.userId),
    });

    // 过滤掉积分历史表存在同样的 uid 不添加, 防止重复添加
    addPointsUserList = addPointsUserList.filter(
      (item) => !pointsHistory.some((v) => v.userId === item.userId),
    );

    if (addPointsUserList.length) {
      for (const item of addPointsUserList) {
        await this.createBase({
          points: item.points,
          desc: item.desc,
          type: PointTypeEnum['系统'],
          userId: item.userId,
          userName: item.name,
          operatorUserId: '0',
          operatorUserName: '系统',
        });
      }
    }
  }
}
